/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ddnsserver;
import java.net.*;
import java.io.*;
import java.util.Vector;
import javax.swing.JTextArea;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import javax.swing.JList;
import javax.xml.stream.*;
/**
 *
 * @author Do Viet Trung
 */
public class SocketClient implements Runnable
{
    private String newline = "\n";
    private Socket sClient;
    private Package pack;
    private String host;
    private String ip;
    private JTextArea txtMessage;
    private JList listUsers;
    private int oldRevision;
    private boolean islogin;
    private AccountInfo user;
    private ArrayList<AccountInfo> listAccount;

    Thread t;

    public SocketClient(Socket sock, JTextArea txtMsg, JList lst, ArrayList<AccountInfo> list)
    {
        listUsers = lst;
        txtMessage = txtMsg;
        sClient = sock;
        islogin = false;
        oldRevision = -1;
        ip = "";
        host = "";
        user = new AccountInfo();
        listAccount = list;
        t = new Thread(this);
        
        //loadAccountInfo(DatabasePath, "u1");
    }

    public void run()
    {
        try
        {
            addListMessage("New client is connecting");
            System.out.println(sClient.getPort());

            InputStream is = sClient.getInputStream();
            BufferedReader br=new BufferedReader(new InputStreamReader(is));

            OutputStream os = sClient.getOutputStream();
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
            try
            {
                String receivedMessage;
                //loop: authenticate user
                while(islogin == false)
                {
                    receivedMessage=br.readLine();
                    pack =  new Package(receivedMessage, Package.TYPE.MESSAGE_OF_CLIENT);
                    loadAccount(pack.getUsername());
                    if(Integer.valueOf(pack.getRevision()) > oldRevision)
                    {
                        oldRevision = Integer.valueOf(pack.getRevision());
                        doOperator(pack, bw);
                    }
                }

                 //Section 2: Send list Host of username
                {
                    for(int i = 0; i < user.getAllowDomainCount(); i++)
                    {
                        sendPackage(Package.CODE.HOST, user.getAllowDomainIndex(i), bw);
                    }
                    //sending finished
                    sendPackage(Package.CODE.OPERATE_SUCCESS, "Send list host finished.", bw);
                }
                //update ip
                do
                {
                    receivedMessage=br.readLine();
                    if(receivedMessage == null)
                    {
                        removeListUsers(pack.getUsername());
                        addListMessage(pack.getUsername() + " logout");
                        sClient.close();
                        bw.close();
                        br.close();
                        continue;
                    }
                    Package newPack =  new Package(receivedMessage, Package.TYPE.MESSAGE_OF_CLIENT);
                    if(oldRevision < Integer.valueOf(newPack.getRevision()))
                    {
                        oldRevision = Integer.valueOf(newPack.getRevision());
                        if(isSameUser(newPack))
                        {
                            doOperator(newPack, bw);
                        }
                        else//not same user
                        {
                            sendPackage(Package.CODE.INVALID_CMD, "invalid command", bw);
                            break;
                        }//end check same user
                    }//end check revision
                } while (true);

                bw.close();
                br.close();
            }finally
            {

//                removeListUsers(pack.getUsername());
//                addListMessage(pack.getUsername() + " logout");
//                sClient.close();
//                bw.close();
//                br.close();
            }
        }catch(Exception ex)
        {
            ex.printStackTrace();
            return;
        }
    }
    private void doOperator(Package newPack, BufferedWriter bw )throws IOException
    {
        String msg = null;
        Package.CODE code = Package.CODE.UNKNOW;
        switch(newPack.getCode())
        {
            case LOGIN:
            {
                if(islogin)break;
                
                if(authenticate(pack.getUsername(), pack.getMessage()) == true)
                {
                    code = Package.CODE.LOGIN;
                    msg = "login success";
                    islogin = true;
                    addListUsers(newPack.getUsername());
                    addListMessage(newPack.getUsername() + " login successful");
                }
                else
                {
                    code = Package.CODE.NOLOGIN;
                    msg = "login failed";
                }
                break;
            }
            case LOGOUT:
            {
                removeListUsers(newPack.getUsername());
                addListMessage(newPack.getUsername() + " is logoff");
                bw.close();
                t.stop();
                break;
            }
            case HOST:
            {
                if(!islogin)break;
                //check permission
                if(hasRightToModifyHost(newPack.getMessage()))
                {
                    host = newPack.getMessage();

                    msg = "permission ok";
                    code = Package.CODE.OPERATE_PERMISSON_OK;
                }
                else
                {
                    msg = "not have permission";
                    code = Package.CODE.OPERATE_PERMISSON_NOT_OK;
                }
                break;
            }
            case UPDATE_IP:
            {
                if(!islogin || host == null || host.equals(""))break;
                if(checkFormatIP(pack.getMessage()))
                {
                    if(update() == true)
                    {
                        msg = "operator successful";
                        code = Package.CODE.OPERATE_SUCCESS;
                    }
                    else
                    {
                        msg = "operator is failed";
                        code = Package.CODE.OPERATE_FAILURE;
                    }
                }
                break;
            }
        }
        if(msg != null && code != Package.CODE.UNKNOW)
        {
            sendPackage(code, msg, bw);
        }
        else
        {
            sendPackage(Package.CODE.INVALID_CMD, msg, bw);
        }
    }
    private void sendPackage(Package.CODE code, String message, BufferedWriter bw)throws IOException
    {
        String sendPackage = pack.createPackage(message, code);
        bw.write(sendPackage);
        bw.newLine();;
        bw.flush();

    }
    //username and password valid
    private boolean authenticate(String username, String password)
    {
        if(user != null)
        {
            boolean valid = checkSingleSignIn(username);
            if(valid == false)return false;
            return user.verify(username, password);
        }
       
        return false;
    }
    private void loadAccount(String username)
    {
        AccountInfo acc = new AccountInfo();
        for(int i = 0; i < listAccount.size(); i++)
        {
            acc = listAccount.get(i);
            if(acc.getUsername().equals(username))
            {
                user = acc;
            }
        }
    }
   
    //check is same user when login
    private boolean isSameUser(Package newPack)
    {
        if(pack.getUsername().equals(newPack.getUsername()))
        {
            return true;
        }
        return false;
    }
    //check user has right to modify this domain
    private boolean hasRightToModifyHost(String host)
    {
        return user.checkPermission(host);
    }
    
    private boolean checkFormatIP(String newIP)
    {
        return true;
    }
    
    private boolean update()
    {
        return true;
    }
     private void addListMessage(String addMsg)
        {
            DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            Date date = new Date();
            String strToday = dateFormat.format(date);
            txtMessage.append("[" + strToday + "]# " + addMsg + newline);
        }
     private void removeListUsers(String username)
     {
        Vector<String> users = new Vector<String>();
          for(int i = 0; i < listUsers.getModel().getSize(); i++)
          {
              String usr = (String)listUsers.getModel().getElementAt(i);
              if(!usr.equals(username))
              {
                users.add(usr);
              }
          }
        listUsers.setListData(users);
     }
     private Vector<String> getListUsers()
    {
         Vector<String> users = new Vector<String>();
          for(int i = 0; i < listUsers.getModel().getSize(); i++)
          {
              String usr = (String)listUsers.getModel().getElementAt(i);
              users.add(usr);
          }
        return users;
     }

     private boolean checkSingleSignIn(String username)
    {
         Vector<String> list = getListUsers();
         for(int i = 0; i < list.size(); i++)
         {
             if(list.elementAt(i).equals(username))
             {
                 return false;
             }
         }
         return true;
     }
     private void addListUsers(String username)
    {
         Vector<String> users = new Vector<String>();
          for(int i = 0; i < listUsers.getModel().getSize(); i++)
          {
                users.add((String)listUsers.getModel().getElementAt(i));
          }
         users.add(username);
         listUsers.setListData(users);
     }

}


